import 'dart:async';
import 'dart:convert';
import 'package:logger/logger.dart';
import 'package:uuid/uuid.dart';
import '../../core/app_initializer.dart';
import '../transport/rpc_transport.dart';
import 'jsonrpc_protocol.dart';
import '../rpc_client.dart';

typedef RpcHandler = FutureOr<dynamic> Function(Map<String, dynamic> params);

class JsonRpcClient implements RpcClient {
  final Logger logger = AppInitializer.logger;

  final RpcTransport transport;
  final _uuid = const Uuid();
  final Map<String, Completer<dynamic>> _pending = {};
  final Map<String, RpcHandler> _handlers = {};

  JsonRpcClient(this.transport) {
    transport.onMessage = _onMessage;
  }

  @override
  void register(String method, RpcHandler handler) {
    _handlers[method] = handler;
  }

  @override
  Future<dynamic> call(
    String method, {
    Map<String, dynamic>? params,
    Duration? timeout,
  }) {
    final effectiveTimeout = timeout ?? const Duration(seconds: 10); // 兜底默认值
    final id = _uuid.v4();
    final request = JsonRpcRequest(method: method, params: params, id: id);
    final c = Completer<dynamic>();
    _pending[id] = c;
    transport.send(jsonEncode(request.toJson()));

    return c.future.timeout(effectiveTimeout, onTimeout: () {
      _pending.remove(id);
      throw JsonRpcError.fromCode(
          JsonRpcErrorCode.serverError, 'Request timeout');
    });
  }

  @override
  void notify(String method, {Map<String, dynamic>? params}) {
    final request = JsonRpcRequest(method: method, params: params);
    transport.send(jsonEncode(request.toJson()));
  }

  Future<void> _onMessage(String payload) async {
    try {
      final msg = jsonDecode(payload);
      if (msg is! Map<String, dynamic> ||
          msg['jsonrpc'] != JsonRpcProtocol.version) {
        throw JsonRpcError.fromCode(JsonRpcErrorCode.invalidRequest);
      }

      if (msg.containsKey('method')) {
        await _handleRequest(msg);
      } else if (msg.containsKey('id')) {
        _handleResponse(msg);
      }
    } catch (e) {
      logger.e('JsonRpcClient parse error: $e');
    }
  }

  Future<void> _handleRequest(Map<String, dynamic> msg) async {
    final method = msg['method'] as String;
    final params = (msg['params'] as Map?)?.cast<String, dynamic>() ?? {};
    final id = msg['id'];
    final handler = _handlers[method];

    if (handler == null) {
      if (id != null) {
        final error = JsonRpcError.fromCode(JsonRpcErrorCode.methodNotFound);
        final response = JsonRpcResponse(error: error, id: id);
        transport.send(jsonEncode(response.toJson()));
      }
      return;
    }

    try {
      final result = await handler(params);
      if (id != null) {
        final response = JsonRpcResponse(result: result, id: id);
        transport.send(jsonEncode(response.toJson()));
      }
    } catch (e) {
      if (id != null) {
        final error =
            JsonRpcError.fromCode(JsonRpcErrorCode.internalError, e.toString());
        final response = JsonRpcResponse(error: error, id: id);
        transport.send(jsonEncode(response.toJson()));
      }
    }
  }

  void _handleResponse(Map<String, dynamic> msg) {
    final id = msg['id']?.toString();
    final completer = _pending.remove(id);
    if (completer == null) return;

    if (msg.containsKey('error')) {
      final err = JsonRpcError.fromJson(msg['error']);
      completer.completeError(err);
    } else {
      completer.complete(msg['result']);
    }
  }

  @override
  void dispose() {
    transport.dispose();
    _pending.clear();
    _handlers.clear();
  }
}
